package de.e2it3t.schulverwaltung.data;

import java.sql.Time;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * Helfer für Zeit Operationen.
 *
 * @author Markus Kunz und Viktor Reiser
 */
public class TimeHelper {

    private TimeHelper() {
    }

    /**
     * Konvertiere eine SQL Zeit in einen String.
     *
     * @param time zu konvertierende Zeit
     *
     * @return String im Format "13:43"
     */
    public static String getTimeString(Time time) {
        if (time == null) {
            return "";
        }

        return new SimpleDateFormat("HH:mm").format(new Date(time.getTime()));
    }

    /**
     * Konvertiere einen Tag in einen sprechenden Namen.
     *
     * @param day 0 - 6 (Montag - Sonntag)
     *
     * @return Name des Tages oder "Unbekannt" bei invalidem Parameter
     */
    public static String getDayString(int day) {
        switch (day) {
            case 0:
                return "Montag";
            case 1:
                return "Dienstag";
            case 2:
                return "Mittwoch";
            case 3:
                return "Donnerstag";
            case 4:
                return "Freitag";
            case 5:
                return "Samstag";
            case 6:
                return "Sonntag";
            default:
                return "Unbekannt";
        }
    }

    /**
     * Zeit aus einem String parsen.
     *
     * @param timeString Zeitstring in der Form "HH:mm" (Minuten sind optional)
     *
     * @return Zeit oder {@code null}, wenn der String ein falsches Format
     * aufweist
     */
    public static Time getTimeFromString(String timeString) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(2000, 1, 1, 0, 0, 0);
        calendar.set(Calendar.MILLISECOND, 0);

        String[] timeSplit = timeString.split("[:]");
        int fields[] = new int[]{
            Calendar.HOUR_OF_DAY, Calendar.MINUTE
        };

        try {
            for (int i = 0; i < Math.min(timeSplit.length, fields.length); i++) {
                calendar.set(fields[i], Integer.parseInt(timeSplit[i].trim()));
            }

            return new Time(calendar.getTimeInMillis());
        } catch (Exception ex) {
            return null;
        }
    }

    /**
     * Vergleiche zwei Zeiten. <br> <br> Der Vergleich wird auf pasis der
     * Stunden und Minuten durchgeführt (keine Sekunden)
     *
     * @param t1 erste Zeit
     * @param t2 zweite Zeit
     *
     * @return negativ, 0 oder positiv wenn die erste Zeit kleiner, gleich oder
     * größer als die zweite ist
     */
    public static int compareTime(Time t1, Time t2) {
        Calendar cal = Calendar.getInstance();

        cal.setTimeInMillis(t1.getTime());
        int h1 = cal.get(Calendar.HOUR_OF_DAY);
        int m1 = cal.get(Calendar.MINUTE);

        cal.setTimeInMillis(t2.getTime());
        int h2 = cal.get(Calendar.HOUR_OF_DAY);
        int m2 = cal.get(Calendar.MINUTE);

        return h1 * 60 + m1 - (h2 * 60 + m2);
    }

    /**
     * Abstand in Minuten zwischen zwei Zeitwerten.
     *
     * @param before frühere Uhrzeit
     * @param after spätere Uhrzeit
     *
     * @return Abstand zwischen Zeitwerten in Minuten (negativ wenn before
     * später ist als after)
     */
    public static int getMinutesDiff(Time before, Time after) {
        return compareTime(after, before);
    }

    /**
     * Addiere Minuten zu einer Uhrzeit.
     *
     * @param time Uhrzeit, zu der addiert wird
     * @param minutes die zu addierenden Minuten (kann auch negativ sein)
     *
     * @return neue Urhzeit mit addierten Minuten
     */
    public static Time addMinutes(Time time, int minutes) {
        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(time.getTime());
        cal.add(Calendar.MINUTE, minutes);

        return new Time(cal.getTimeInMillis());
    }

    /**
     * Erhalte die frühere Uhrzeit.
     * 
     * @param t1 erste Uhrzeit
     * @param t2 zweite Uhrzeit
     * 
     * @return die frühere Uhrzeit von beiden
     */
    public static Time minTime(Time t1, Time t2) {
        return compareTime(t1, t2) < 0 ? t1 : t2;
    }

    /**
     * Erhalte die spätere Uhrzeit.
     * 
     * @param t1 erste Uhrzeit
     * @param t2 zweite Uhrzeit
     * 
     * @return die spätere Uhrzeit von beiden
     */
    public static Time maxTime(Time t1, Time t2) {
        return compareTime(t1, t2) > 0 ? t1 : t2;
    }

    /**
     * Korrigiere eine Uhrzeit auf ein Minimum und Maximum.
     * 
     * @param min minimal frühstmögliche Uhrzeit
     * @param max maximal spätmöglichste Uhrzeit
     * @param value die zu korrigierende Uhrzeit
     * 
     * @return wenn die gegebene Uhrzeit das Minimum nicht unterschreitet bzw.
     * das Maximum nicht überschreitet, dann die Uhrzeit selbst, ansonsten
     * Minimum oder Maximum
     */
    public static Time minMaxTime(Time min, Time max, Time value) {
        return maxTime(max, minTime(min, value));
    }

    /**
     * Korrigiere eine Uhrzeit an ein minütliches Interval. <br>
     * <br>
     * Wenn z.B. das Interval 5 gegeben ist, dann können nur Uhrzeiten wie
     * 12:05, 22:45, etc rauskommen.
     * 
     * @param time die zu korrigierende Uhrzeit
     * @param interval Interval in Minuten (sollte ein Teiler von 60 sein!)
     * 
     * @return Uhrzeit 
     */
    public static Time clampToMinutesInterval(Time time, int interval) {
        // kein ganzzahliger Teiler von 60
        if ((60 / interval) * interval != 60) {
            throw new IllegalArgumentException();
        }
        
        if (time == null) {
            throw new NullPointerException();
        }

        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(time.getTime());
        cal.set(Calendar.MILLISECOND, 0);

        int minutes = cal.get(Calendar.MINUTE);

        cal.set(Calendar.MINUTE, minutes / interval * interval);

        if (minutes % interval >= interval / 2.0) {
            cal.add(Calendar.MINUTE, interval);
        }

        return new Time(cal.getTimeInMillis());
    }

    /**
     * Runde ein Minutenanzahl auf ein Interval. <br>
     * <br>
     * Wenn ich z.B. das Interval 12 gegeben habe, dann erhalte ich auf jeden 
     * Fall eine der Zahlen 0, 12, 24, 36, ...
     * 
     * @param minutes Minuten (oder allgemein ein Zahl)
     * @param interval Interval in Minuten (oder Teiler der Minuten)
     * 
     * @return auf das Interval gerundete Minuten
     */
    public static int clampToMinutesInterval(int minutes, int interval) {
        int result = minutes / interval * interval;

        if (minutes % interval >= interval / 2.0) {
            result += interval;
        }

        return result;
    }
}
